package com.flute.framework.search.result.xmlresult;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 解析xml的类
 * 
 * @author xuweiwei
 * 
 */

public class XmlParser {
	private static XmlParser xmlParser = null;

	private XmlParser() {
	}

	public static synchronized XmlParser getInstance() {
		if (xmlParser == null) {
			xmlParser = new XmlParser();
		}
		return xmlParser;
	}

	/**
	 * 根据xpath表达式，返回xml字符串
	 * 
	 * @param pathStrs
	 *            节点的xpath表达式
	 * @return xml字符串
	 */
	public String parser(String[] pathStrs) {
		return parser(pathStrs, null, Constants.DEFAULT_ENCODING,
				Constants.DEFAULT_VERSION);
	}

	/**
	 * 根据节点的xpath表达式和值，返回xml字符串
	 * 
	 * @param pathStrs
	 *            节点的xpath表达式
	 * @param content
	 *            节点值
	 * @return xml字符串
	 */
	public String parser(String[] pathStrs, String[] content) {
		return parser(pathStrs, content, Constants.DEFAULT_ENCODING,
				Constants.DEFAULT_VERSION);
	}

	/**
	 * 根据节点的xpath表达式和xml文件的字符编码、字符，返回xml字符串
	 * 
	 * @param pathStrs
	 *            节点的xpath表达式
	 * @param encoding
	 *            xml字符编码
	 * @param version
	 *            xml版本
	 * @return xml字符串
	 */
	public String parser(String[] pathStrs, String encoding, String version) {
		return parser(pathStrs, null, encoding, version);
	}

	/**
	 * 根据节点的xpath表达式和值，以及xml文件的字符编码、字符，返回xml字符串
	 * 
	 * @param pathStrs
	 *            节点的xpath表达式
	 * @param content
	 *            节点值
	 * @param encoding
	 *            xml字符编码
	 * @param version
	 *            xml版本
	 * @return xml字符串
	 */
	public String parser(String[] pathStrs, String[] content, String encoding,
			String version) {
		StringBuilder result = new StringBuilder();
		String head = getHead(encoding, version);
		Node root = getNode(pathStrs, null, content);
		String xmlContent = root.toXmlString();
		result.append(head);
		result.append(xmlContent);
		return result.toString();
	}

	/**
	 * 通过编码集和版本获取xml文件头的字符串
	 * 
	 * @param encoding
	 *            xml文件编码集
	 * @param version
	 *            xml文件版本
	 * @return xml文件头的字符串
	 */
	public String getHead(String encoding, String version) {
		if (encoding == null || encoding.trim().length() <= 0) {
			encoding = Constants.DEFAULT_ENCODING;
		}
		if (version == null || encoding.trim().length() <= 0) {
			version = Constants.DEFAULT_VERSION;
		}
		return "<?xml version=\"" + version + "\" encoding=\"" + encoding
				+ "\" ?>";
	}

	/**
	 * 根据节点的xpath表达式和值获得该节点
	 * 
	 * @param strs
	 *            节点的xpath表达式
	 * @param root
	 *            根节点
	 * @param contents
	 *            节点值
	 * @return 节点
	 */
	public Node getNode(String[] strs, Node root, String[] contents) {
		if (strs != null && strs.length > 0) {
			for (int i = 0; i < strs.length; i++) {
				String[] splits = split(strs[i]);
				Node father = null;
				for (int j = 0; j < splits.length; j++) {
					String nodeName = splits[j];
					String content = null;
					if (contents != null && contents.length > i) {
						content = contents[i];
					}
					if (j == 0) {
						root = processRootNode(nodeName, root, strs[i],
								splits.length == 1 ? content : null);
						father = root;
					} else {
						if (isAttribute(nodeName)) {
							if (j < splits.length - 1) {
								throw new XmlParserException(
										"attribute  error:" + strs[i]);
							} else {
								father = processAtt(nodeName, father, strs[i],
										content);
							}

						} else {
							father = processNode(father, nodeName, content,
									j == splits.length - 1 ? true : false);
						}
					}
				}
			}
		}
		return root;
	}

	/**
	 * 判断字符串是否是属性名
	 * 
	 * @param nodeName
	 *            节点或属性名称
	 * @return 是否是属性名
	 */
	private boolean isAttribute(String nodeName) {
		return nodeName.startsWith(Constants.ATTRIBUTE_PREFIX);
	}

	/**
	 * 
	 * @param nodeName
	 *            节点名称
	 * @param root
	 *            根节点
	 * @param lineData
	 *            xpath表达式
	 * @param content
	 *            节点值
	 * @return 节点
	 */
	private Node processRootNode(String nodeName, Node root, String lineData,
			String content) {
		Node curr = new Node(nodeName, content);
		if (root == null) {
			root = curr;
		} else if (!root.equals(curr)) {
			throw new XmlParserException("error root :" + lineData);
		} else {
			// do something
		}
		return root;
	}

	/**
	 * 为指定节点增加属性
	 * 
	 * @param nodeName
	 *            属性名
	 * @param father
	 *            父节点
	 * @param lineData
	 *            源数据
	 * @param content
	 *            属性值
	 * @return 节点
	 */
	private Node processAtt(String nodeName, Node father, String lineData,
			String content) {
		Attribute a = new Attribute(nodeName.substring(1), content, father);
		for (int i = 0; i < father.getAttList().size(); i++) {
			if (father.getAttList().get(i).equals(a)) {
				if (father.isRoot()) {
					throw new XmlParserException("root error Attribute:"
							+ lineData);
				} else {
					Node curr = processNode(father.getFather(), father
							.getName(), null, true);
					father = curr;
				}
			}
		}
		father.getAttList().add(a);
		return father;
	}

	/**
	 * 通过节点的多个属性生成节点
	 * 
	 * @param nodeName
	 *            节点名
	 * @param father
	 *            父节点
	 * @param content
	 *            值
	 * @param addflag
	 *            shi
	 * @return 节点
	 */
	private Node processNode(Node father, String nodeName, String content,
			boolean addflag) {
		Node curr = new Node(nodeName, content, father);
		if (!father.isFather(curr)) {
			father.getSubNodeList().add(curr);
		} else {
			int count = 0;
			Node last = null;
			for (int k = 0; k < father.getSubNodeList().size(); k++) {
				if (father.getSubNodeList().get(k).getName().equals(
						curr.getName())) {
					count++;
					last = father.getSubNodeList().get(k);
				}
			}
			if (addflag) {
				father.getSubNodeList().add(curr);
			} else {
				curr = last;
			}
		}
		return curr;
	}

	/**
	 * 把数据分割为xpath表达式数组
	 * 
	 * @param lineData
	 *            源数据
	 * @return xpath表达式数组
	 */
	private String[] split(String lineData) {
		String[] spliteds = null;
		String regEx = "//([^/]+)(/[^/]+)*";
		Pattern p = Pattern.compile(regEx);
		Matcher m = p.matcher(lineData);
		if (!m.matches()) {
			throw new XmlParserException("format error :" + lineData);
		} else {
			spliteds = lineData.substring(2).split("/");
		}
		return spliteds;
	}
}
